Android 动态绘制圆形进度条

效果图

此处输入图片的描述

实现思路

  • 在画布上直接绘制View,需要了解一下几点
    • 1.需要画一个底层小圆和上层大圆
    • 2.圆圈上有不同进度的颜色
    • 3.颜色的变化规律是先慢慢变多再慢慢减少

### 一、画圆

  • 需要使用Canvas的该方法

    public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,
               @NonNull Paint paint) {
           drawArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, useCenter,
                   paint);
       }
    
  • 如下画出了默认的背景圆圈:

    // 设置画笔相关属性
    mPaint.setAntiAlias(true);  //抗锯齿
    mPaint.setColor(Color.rgb(0x3a, 0x58, 0x5f));
    canvas.drawColor(Color.TRANSPARENT); //设置背景透明
    mPaint.setStrokeWidth(mCircleLineStrokeWidthBottom);
    mPaint.setStyle(Paint.Style.STROKE);
    
     // 位置
     mRectF.left = mCircleLineStrokeWidth / 2; // 左上角x
     mRectF.top = mCircleLineStrokeWidth / 2; // 左上角y
     mRectF.right = width - mCircleLineStrokeWidth / 2; // 左下角x
     mRectF.bottom = height - mCircleLineStrokeWidth / 2; // 右下角y
    
     // 绘制圆圈,进度条背景
     canvas.drawArc(mRectF, -90, 360, false, mPaint); //顺时针为正,起始点-90是视图上最高的点
    

二、画进度圆弧

  • 其实实现很简单,换另外一种颜色同样在画布上画出即可,支持此时画的不是360°,而是通过进度计算出来的一个圆弧。

    //绘制上面可变的进度条
    mPaint.setColor(Color.rgb(0x2f, 0xc2, 0xe6));
    mPaint.setStrokeWidth(mCircleLineStrokeWidthAbove);
    
    if (isOddNumber) { //奇数轮时
        canvas.drawArc(mRectFAbove, -90, ((float) mProgress / mMaxProgress) * 360, false, mPaint);
    } else {          //偶数轮时,反方向绘制
        canvas.drawArc(mRectFAbove, -90, (-(float) mProgress / mMaxProgress) * 360, false, mPaint);
    }
    

三、定时器调用

  • 设置定时器

    private final int TIME_PROGRESS = 30;
    private int curProgress = 0;
    private boolean isOddNumber= false; //是否是奇数阶段
    private Handler handler = new Handler();
    Runnable runnableProgress = new Runnable() {
    
        @Override
        public void run() {
    
            if (circleProgressView != null) {
    
                if (curProgress == 0) { //定义奇数和偶数阶段
                    isOddNumber = true;
                } else if (curProgress >= 100) {
                    isOddNumber = false;
                }
    
                if (isOddNumber) { //奇数阶段累加1
                    curProgress += 1;
                } else {           //偶数阶段递减1
                    curProgress -= 1;
                }
    
                circleProgressView.setProgress(curProgress, isOddNumber);
    
            }
            handler.postDelayed(this, TIME_PROGRESS); //handler自带方法实现定时器
        }
    };
    
  • 启动

    handler.postDelayed(runnableProgress, TIME_PROGRESS);//设置转圈进度定时器
    
  • 关闭

    handler.removeCallbacks(runnableProgress);  //取消转圈进度定时器线程
    

四、总结

  • 其实很多自定义的View都可以用Canvas直接画出来,只要研究透了Canvas的使用和原理。

五、最后附上源码

  • 源码下载地址:https://github.com/yygmind/AndroidStudy
  • CircleProgressView

    public class CircleProgressView extends View {
    
        private static final String TAG = "CircleProgressView";
    
        private int mMaxProgress = 100;
    
        private int mProgress = 30;
    
        private final int mCircleLineStrokeWidthBottom = 10;
        private final int mCircleLineStrokeWidthAbove = 20;
    
        // 画圆所在的距形区域
        private final RectF mRectFBottom;
        private final RectF mRectFAbove;
    
        private final Paint mPaint;
    
        private final Context mContext;
        private int width;
        private int height;
    
        private boolean isOddNumber= false; //是否是奇数阶段
    
        public CircleProgressView(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            mContext = context;
            mRectFBottom = new RectF();
            mRectFAbove = new RectF();
            mPaint = new Paint();
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            width = this.getWidth();
            height = this.getHeight();
    
            if (width != height) {
                int min = Math.min(width, height);
                width = min;
                height = min;
            }
    
            // 设置背景画笔相关属性
            mPaint.setAntiAlias(true);  //抗锯齿
            mPaint.setColor(Color.rgb(0x3a, 0x58, 0x5f));
            canvas.drawColor(Color.TRANSPARENT); //设置背景透明
            mPaint.setStrokeWidth(mCircleLineStrokeWidthBottom);
            mPaint.setStyle(Paint.Style.STROKE);
            // 位置
            setRectFPosition(mRectFBottom, mCircleLineStrokeWidthBottom);
            setRectFPosition(mRectFAbove, mCircleLineStrokeWidthAbove);
    
            // 绘制圆圈,进度条背景
            canvas.drawArc(mRectFBottom, -90, 360, false, mPaint); //顺时针为正,起始点-90是视图上最高的点
    
            //绘制上面可变的进度条
            mPaint.setColor(Color.rgb(0x2f, 0xc2, 0xe6));
            mPaint.setStrokeWidth(mCircleLineStrokeWidthAbove);
    
            if (isOddNumber) { //奇数轮时
                canvas.drawArc(mRectFAbove, -90, ((float) mProgress / mMaxProgress) * 360, false, mPaint);
            } else {           //偶数轮时,反方向绘制
                canvas.drawArc(mRectFAbove, -90, (-(float) mProgress / mMaxProgress) * 360, false, mPaint);
            }
    
        }
    
        /**
         * 设置RectF位置
         * @param mRectF
         * @param mCircleLineStrokeWidth
         */
        private void setRectFPosition(RectF mRectF, int mCircleLineStrokeWidth) {
            mRectF.left = mCircleLineStrokeWidth / 2; // 左上角x
            mRectF.top = mCircleLineStrokeWidth / 2; // 左上角y
            mRectF.right = width - mCircleLineStrokeWidth / 2; // 左下角x
            mRectF.bottom = height - mCircleLineStrokeWidth / 2; // 右下角y
        }
    
        public int getMaxProgress() {
            return mMaxProgress;
        }
    
        public void setMaxProgress(int maxProgress) {
            this.mMaxProgress = maxProgress;
        }
    
        public void setProgress(int progress, boolean isOddNumber) {
            this.mProgress = progress;
            this.isOddNumber = isOddNumber;
            this.invalidate();
        }
    
        public void setProgressNotInUiThread(int progress) {
            this.mProgress = progress;
            this.postInvalidate();
        }
    
    }
    
  • Xml中配置:

    <com.cody.myandroidstudy.view.CircleProgressView
            android:id="@+id/circle_progress_view"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_centerInParent="true"/>
    
  • Activity中使用:

    public class CircleProgressActivity extends BaseActivity {
    
        private final int TIME_PROGRESS = 30;
        private int curProgress = 0;
        private boolean isOddNumber= false; //是否是奇数阶段
        private Handler handler = new Handler();
        Runnable runnableProgress = new Runnable() {
    
            @Override
            public void run() {
    
                if (circleProgressView != null) {
    
                    if (curProgress == 0) {  //定义奇数和偶数阶段
                        isOddNumber = true;
                    } else if (curProgress >= 100) {
                        isOddNumber = false;
                    }
    
                    if (isOddNumber) { //奇数阶段累加1
                        curProgress += 1;
                    } else {           //偶数阶段递减1
                        curProgress -= 1;
                    }
    
                    circleProgressView.setProgress(curProgress, isOddNumber);
    
                }
                handler.postDelayed(this, TIME_PROGRESS); //handler自带方法实现定时器
            }
        };
        private CircleProgressView circleProgressView;
    
    @Override
    public void initView() {
        setContentView(R.layout.activity_circle_progress);
        circleProgressView = (CircleProgressView) findViewById(R.id.circle_progress_view);
    }

    @Override
    public void initData() {
    }

    @Override
    public void initListener() {
    }

    @Override
    public void progress(View v) {
    }

    @Override
    protected void onStart() {
        super.onStart();
        handler.postDelayed(runnableProgress, TIME_PROGRESS);//设置转圈进度定时器
    }

    @Override
    protected void onStop() {
        super.onStop();
        handler.removeCallbacks(runnableProgress);  //取消转圈进度定时器线程
    }
}